import { keepDecimalFixed } from "../../utils";
import { CircularQueue } from "../../lib/quant/common/CircularQueue";
import { Kline, Indicator } from "../../lib/quant/interface";
import { Quant } from "../../lib/quant";
import { MA } from "../../lib/quant/indicator/ma";
import { calculateSlopeSum, createDelayCount } from "../util";

export type StatisticsResult = {
  min: number
  max: number
  max_volume: number
  min_volume: number
  /** 异常k线占比 */
  exceptional_undulation_rate: number;
  /** 波动率 */
  // volatility: number;
  /** 斜率 */
  sum_slope: number;
  /** ma黏着率 */
  adhesion_ma_rate: number
  point_status: number
  point_peaks: number
  point_valleys: number

}
function isUpperShadow(kLine: Kline): boolean {
  // 上影针的判断逻辑：上影线较长，通常认为长度超过实体长度，且实体长度为正（收盘价低于开盘价）  
  const upperShadowLength = kLine.high - kLine.close;
  const bodyLength = kLine.open - kLine.close;
  return upperShadowLength > bodyLength && bodyLength > 0;
}

function isLowerShadow(kLine: Kline): boolean {
  // 下影针的判断逻辑：下影线较长，通常认为长度超过实体长度，且实体长度为负（收盘价高于开盘价）  
  const lowerShadowLength = kLine.close - kLine.low;
  const bodyLength = kLine.close - kLine.open;
  return lowerShadowLength > Math.abs(bodyLength) && bodyLength < 0;
}

/**
 * 常规统计(不考虑量的指标)，最高、最低
 */
export class Statistics implements Indicator {
  buffer: CircularQueue<Kline>;
  result: CircularQueue<StatisticsResult>;
  period: number;
  maxHistoryLength = 1000;
  amplitude_threshold: number;
  slope_threshold: number;
  positionCount: number;
  _quant: Quant
  delayCount = createDelayCount(5);
  constructor({ period, amplitude_threshold, maxHistoryLength, positionCount, slope_threshold }: { 
    period: number, 
    amplitude_threshold: number, 
    maxHistoryLength?: number, 
    positionCount?: number,
    slope_threshold?: number,
  }) {
    this.maxHistoryLength = maxHistoryLength || this.maxHistoryLength;
    this.positionCount = positionCount || 7
    this.slope_threshold = slope_threshold || 0.006
    this.period = period;
    this.amplitude_threshold = amplitude_threshold;
    this.buffer = new CircularQueue(period);
    this.result = new CircularQueue(this.maxHistoryLength);
  }
  getLeft(i: number, p: number) {
    const scope = (this.buffer.get(i).close - this.buffer.get(i - p).close) / this.buffer.get(i - p).close
    return scope;
  }
  getRight (i: number, p: number)  {
    const scope = (this.buffer.get(i + p).close - this.buffer.get(i).close) / this.buffer.get(i).close
    return scope;
  }
  getStatistics() {
    if (this.buffer.size() < 1 || this.result.size() === 0) {
      return {
        max: this.buffer.get(0).high,
        min: this.buffer.get(0).low,
        max_volume: this.buffer.get(0).volume,
        min_volume: this.buffer.get(0).volume,
        exceptional_undulation_rate: 0,
        adhesion_ma_rate: 0,
        point_status: 0,
        point_peaks: 0,
        point_valleys: 0,
      } as StatisticsResult;
    }
    const ma = (this._quant.getIndicator('ma240') as MA) || (this._quant.getIndicator('ma120') as MA) || (this._quant.getIndicator('ma60') as MA);

    let max = this.buffer.get(0).high;
    let min = this.buffer.get(0).low;
    let { max_volume, min_volume } = this.result.getLast()
    let exceptional_undulation_count = 0;
    let adhesion_ma = 0;
    let point_status: -1 | 1 | 0 = 0;

    // 计算收益率  
    const returns: number[] = [];
    // 针尖
    const peaks = [];
    const valleys = []; // 谷
    let _leftSlope = 0;
    let _rightSlope = 0;
    //  最新的period个数据求和
    for (let i = 1; i < this.buffer.size(); i++) {
      const current = this.buffer.get(i);
      const pre = this.buffer.get(i - 1);

      const returnRate = (current.close - pre.close) / pre.close;
      // 振幅
      const amplitude_abs = Math.abs((current.high - current.low) / current.low);
      returns.push(Math.abs(returnRate));

      if (current.high > max) {
        max = current.high
      } else if (current.low < min) {
        min = current.low
      }
      if (current.volume > max_volume) {
        max_volume = current.volume
      } else if (current.volume < min_volume) {
        min_volume = current.volume
      }
      if (amplitude_abs > this.amplitude_threshold && (isUpperShadow(current) || isLowerShadow(current) || amplitude_abs > this.amplitude_threshold * 1.5)) {
        exceptional_undulation_count++;
      }
      if (!isNaN(ma.getValue()) && current.high > ma.getValue() && current.low < ma.getValue()) {
        adhesion_ma++;
      }
      // 判断当前index，左偏移7，右偏移7，是否有波峰波谷 
      const positionCount = this.positionCount;
      if (this.delayCount.trigger() && this.buffer.get(i - positionCount) && this.buffer.get(i + positionCount)  && i - positionCount > 0 && i + positionCount < this.buffer.size()) {
        const leftSlope = this.getLeft(i, positionCount);
        const rightSlope = this.getRight(i, positionCount);

        const distance = this.slope_threshold;
        _leftSlope=leftSlope
      _rightSlope=rightSlope
        const min_distance = this.slope_threshold / 1.5
        const max_distance = distance * 1.5
        if ((leftSlope < -distance && rightSlope > distance)
          || (leftSlope < -min_distance && rightSlope > max_distance)
          || (leftSlope < -max_distance && rightSlope > min_distance)
        ) {
          valleys.push(leftSlope);
          this.delayCount.reset();
        } else if (
          (leftSlope > distance && rightSlope < -distance)
          || (leftSlope > min_distance && rightSlope < -max_distance)
          || (leftSlope > max_distance && rightSlope < -min_distance)
        ) {
          peaks.push(rightSlope);
          this.delayCount.reset();
        }
      }
    }

    // 异常率
    const exceptional_undulation_rate = keepDecimalFixed(exceptional_undulation_count / this.buffer.size(), 4);
    if (peaks.length > valleys.length && peaks.length > 1) {
      point_status = -1
    } else if (peaks.length < valleys.length && valleys.length > 1) {
      point_status = 1
    }
    // 计算标准差（波动率）  
    // const mean = returns.reduce((sum, val) => sum + val, 0) / returns.length;  
    // const sumOfSquares = returns.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0);  
    // const variance = sumOfSquares / (returns.length - 1);  
    // const volatility = keepDecimalFixed(Math.max(...returns) / ((max - min) / min), 4); 
    // console.log(this.buffer.getLast().close - this.buffer.get(0).close, this.period * (max - min))
    return {
      max,
      min,
      max_volume: max_volume,
      min_volume: min_volume,
      exceptional_undulation_rate: exceptional_undulation_rate,
      adhesion_ma_rate: keepDecimalFixed(adhesion_ma / this.buffer.size(), 4),
      point_status: point_status,
      point_peaks: peaks.length,
      point_valleys: valleys.length,
      // point_peaks: _leftSlope,
      // point_valleys: _rightSlope,
    } as StatisticsResult;
  }
  add(data: Kline) {
    if (!('high' in data) || !('low' in data)) {
      console.warn(`high or low not in ${data}`)
      return
    }
    // 添加到临时数组中
    this.buffer.push(data);
    const result = this.getStatistics();
    this.result.push(result);
    return result;
  }

  updateLast(data: Kline) {
    if (!('high' in data) || !('low' in data)) {
      console.warn(`high or low not in ${data}`)
      return
    }
    // if ((data as any).isMock) {
    //   return
    // }
    this.buffer.update(this.buffer.size() - 1, data);
    const result = this.getStatistics();
    // 更新最后一个
    this.result.update(this.result.size() - 1, result);
    return result;
  }

  getValue(index = -1) {
    if (index < 0) {
      return this.result.get(this.result.size() + index);
    }
    return this.result.get(index);
  }
}
